JavaScript 捉摸不定的 This


Posted by Calon on 2022-04-26

this 大部份跟 function 怎麼宣告沒有關聯,跟怎麼調用 function 比較有關。

var someone = '全域';

function callSomeone (){
    console.log(this.someone);
};

callSomeone();
// 這邊是用 simple call 的方式調用 function
// 裡面的 this 會指向 window 中的 someone
// 所以這邊會印出 '全域'

const obj = {
    someone: '物件',
    callSomeone
};

obj.callSomeone();
// 這邊是 物件的方法調用
// 裡面的 this 會指向前面物件 obj 裡的 someone
// 所以這邊會印出 '物件'


const person = 'window';

function callPerson(){
    console.log(this.person);
};

callPerson();
// 這邊是用 simple call 的方式調用 function
// 裡面的 this 會指向 window 中的 person
// 但變數 person 宣告時是用 const,const 不會污染全域變數
// 所以 window 裡面沒有變數 person
// 這邊結果會是 undefine

const obj2 = {
    person: 'object',
    callPerson
};
obj2.callPerson();
// 這邊是 物件的方法調用
// 裡面的 this 會指向前面物件 obj2 裡的 person
// 所以這邊會印出 'object'

setTimeout(callSomeone, 1000);
(callPerson)();
// 這 2 個調用方式也是 simple call
// 所以 this 會指向 window
// 結果會是 undefine


比較好記的方式:看 function 前面的物件是誰,this 就會指向該物件。

function callSomeone (){
  console.log(this.someone);
};

const obj = {
  someone: '物件',
  callSomeone,
  innerObj: {
    someone: '內層物件',
    callSomeone,
    innermostObj: {
      someone: '深處物件',
      callSomeone,
    }
  }
};

obj.callSomeone();
// 這邊調用 callSomeone() 時前面的物件是 obj
// 所以裡面的 this 會指向前面 obj
// 這邊會印出 '物件'

obj.innerObj.callSomeone();
// 這邊調用 callSomeone() 時前面的物件是 innerObj
// 所以裡面的 this 會指向前面 innerObj
// 這邊會印出 '內層物件'

obj.innerObj.innermostObj.callSomeone();
// 這邊調用 callSomeone() 時前面的物件是 innermostObj
// 所以裡面的 this 會指向前面 innermostObj
// 這邊會印出 '深處物件'


影響 this 的語法

Javascript 有 3 種方法可以強制綁定 this 的指向,分別是:

  • call()
  • apply()
  • bind()

call()

call() 傳入參數後會立即執行 function。

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(){
    console.log(this.myName);
  }
};


obj2.fn.call(obj1); // 'object1'

另外,call() 除了 this 參數以外也可以代入其他參數。

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};


obj2.fn.call(obj1, 'Hi'); // 'Hi, object1'
// 這裡第 1 個參數是要綁定的 this 參數
// 第 2 個參數是 fn() 的參數

apply()

apply()call() 非常相似,不同的是 apply() 除了要綁定的 this 參數之外是代入 array。

fun.apply(thisArg, [argsArray])

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(msg1, msg2){
    console.log(msg1 + ', ' + this.myName + '. ' + msg2);
  }
};

obj2.fn.apply(obj1, ['Hi', 'How about you?']);
// 'Hi, object1. How about you?'

bind()

bind() 也和 call() 相似,差別在於 bind() 不會馬上執行函式,而是回傳綁定好 this 的 function。

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};


const newFn = obj2.fn.bind(obj1, 'Hi');
newFn();
// 'Hi, object1.

如果作為 this 的參數所傳入的值不是 Object

這時這些值會被轉為相關的建構方法:

function fn() {
  console.log(this);
}

fn.call(3); // this = new Number(3)
fn.apply('hi'); // this = new String('hi')
fn.bind(true)(); // this = new Boolean(true)


將 undefined、null 傳入 call()、apply()、bind()

當我們在執行 call()apply()bind() 時把綁定 this 的參數代入 undefined、null,這時 function 裡的 this 將會重新指向 window

var myName = 'window';

const obj = {
  myName: 'object',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};

obj.fn.call(undefined, 'Hi'); // 'Hi, window'
obj.fn.apply(undefined, ['Hi']); // 'Hi, window'
const newFn = obj.fn.bind(null, 'Hi');
newFn(); // 'Hi, window'
// 'Hi, object1.

而在嚴謹模式下則會出錯。

'use strict'
var myName = 'window';

const obj = {
  myName: 'object',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};

obj.fn.call(undefined, 'Hi');
// Uncaught TypeError:
// Cannot read properties of undefined (reading 'myName')


箭頭函式的 this

箭頭函式本身沒有 this,箭頭函式的 this 會指向外層作用域的 this

var person = 'window';

const callPerson = () => {
    console.log(this.person);
};

const obj = {
    person: 'object',
    callPerson
};
obj.callPerson();
// 箭頭函式的 this 會指向外層作用域的 this
// 這邊外層沒有作用域
// 所以這裡的 this 會指向 window


const obj2 = {
  person: 'object2',
  fn() {
    (() => {
      console.log(this.person);
    })();
  }
};

obj2.fn();
// obj2.fn 內的箭頭函式的 this 會指向 obj2.fn() 的 this
// 所以這邊會印出 'object2'

參考資料

#this #javascript







Related Posts

Leetcode JS 2676. Throttle

Leetcode JS 2676. Throttle

如何使用 Python 學習機器學習(Machine Learning)

如何使用 Python 學習機器學習(Machine Learning)

1789. Primary Department for Each Employee

1789. Primary Department for Each Employee


Comments